#include "Wt.h"

#include <stdio.h>

module MODULE_VAR_EXPORT wtcl_module;

static int wtcl_handler(request_rec *r)
{
  int rc;
  ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r->server, "Wtcl: wtcl_handler.");
  rc = WtProcessRequest(r);
  ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r->server,
    "Wtcl: Returning %d from wtcl_handler.", rc);
  return rc;
}

static void wtcl_init(server_rec *s, pool *p)
{
  FILE *dbg;
  /* char *ver; */

  ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, s, "Wtcl: wtcl_init");

  dbg = fopen("c:\\tmp\\wtcl.txt", "w");
  fprintf(dbg, "Tcl init");
  fflush(dbg);
  fclose(dbg);

  /*
  if ((ver = Tcl_InitStubs(interp, "8.1", 0)) == NULL) {
    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERROR, s,
      "Wtcl: Tcl_InitStubs failed."); 
    return TCL_ERROR;
  }
  */
}

static void *wtcl_create_dir_config(pool *p, char *path)
{
  Tcl_Obj *conf = WtNewTableObj(NULL);

  return conf;
}

static void *wtcl_merge_dir_config(pool *p, void *basev, void *overridesv)
{
  Tcl_Obj *conf = WtNewTableObj(NULL);
  Tcl_Obj *base = (Tcl_Obj *)basev;
  Tcl_Obj *overrides = (Tcl_Obj *)overridesv;

  WtUpdateTable(conf, base);
  WtUpdateTable(conf, overrides);

  return conf;
}

static void *wtcl_create_server_config(pool *p, server_rec *s)
{
  Tcl_Obj *conf;

  ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, s,
    "Wtcl: wtcl_create_server_config");

  return WtNewTableObj(NULL);
}

static void *wtcl_merge_server_config(pool *p, void *basev, void *overridesv)
{
  Tcl_Obj *conf = WtNewTableObj(NULL);
  Tcl_Obj *base = (Tcl_Obj *)basev;
  Tcl_Obj *overrides = (Tcl_Obj *)overridesv;

  WtUpdateTable(conf, base);
  WtUpdateTable(conf, overrides);

  return conf;
}

static void wtcl_child_init(server_rec *s, pool *p)
{
  ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, s, "Wtcl: wtcl_child_init"); 
  WtInitServer(s, p);
}

static void wtcl_child_exit(server_rec *s, pool *p)
{
  ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, s, "Wtcl: wtcl_child_exit"); 
  WtEndServer(s, p);
}

static const char *WtSystemPackageCfg(cmd_parms *cmd, Tcl_Obj *dirConf,
    char *name, char *ver, char *exact)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;
  Tcl_Obj *pkg = WtNewTableObj(NULL);

  if (exact && strcasecmp(exact, "exact")) {
    return "WtSystemPackage: Optional third parameter can only be \"exact\". Usage: WtSystemPackage NAME [VERSION [exact]]";
  }

  WtTableSetStrToStr(pkg, "name", name);
  WtTableSetStrToStr(pkg, "version", ver);
  WtTableSetStrToBool(pkg, "exact", exact != NULL);

  WtTableSetStrToObj(destConf, "package", pkg);

  return NULL;
}

static const char *WtReuseInterpCfg(cmd_parms *cmd, void *dirConf, int flag)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;

  WtTableSetStrToBool(destConf, "reuseInterp", flag);

  return NULL;
}

static const char *WtInterpCreateCfg(cmd_parms *cmd, void *dirConf, char *arg)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;

  WtTableSetStrToStr(destConf, "interpCreateScript", arg);

  return NULL;
}

static const char *WtInterpDestroyCfg(cmd_parms *cmd, void *dirConf, char *arg)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;

  WtTableSetStrToStr(destConf, "interpDestroyScript", arg);

  return NULL;
}

static const char *WtReqStartCfg(cmd_parms *cmd, void *dirConf, char *arg)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;

  WtTableSetStrToStr(destConf, "requestStartScript", arg);

  return NULL;
}

static const char *WtReqEndCfg(cmd_parms *cmd, void *dirConf, char *arg)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;

  WtTableSetStrToStr(destConf, "requestEndScript", arg);

  return NULL;
}

static const char *WtReqErrorCfg(cmd_parms *cmd, void *dirConf, char *arg)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;

  WtTableSetStrToStr(destConf, "requestErrorScript", arg);

  return NULL;
}

static const char *WtPageStartCfg(cmd_parms *cmd, void *dirConf, char *arg)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;

  WtTableSetStrToStr(destConf, "pageStartScript", arg);

  return NULL;
}

static const char *WtPageEndCfg(cmd_parms *cmd, void *dirConf, char *arg)
{
  Tcl_Obj *serverConf = ap_get_module_config(cmd->server->module_config,
    &wtcl_module);
  Tcl_Obj *destConf = cmd->path ? dirConf : serverConf;

  WtTableSetStrToStr(destConf, "pageEndScript", arg);

  return NULL;
}

static const handler_rec wtcl_handlers[] =
{
  {"application/x-httpd-wtcl", wtcl_handler},
  {NULL}
};

static const command_rec wtcl_cmds[] =
{
  {"WtSystemPackage", WtSystemPackageCfg, NULL, OR_FILEINFO, TAKE13,
    "Set the Wtcl package name. Usage: WtPackage NAME [VERSION [exact]]"},
  {"WtReuseInterp", WtReuseInterpCfg, NULL, OR_FILEINFO, FLAG,
    "Avoid creating a new interpreter for each request. Usage: WtReuseInterp on | off"},
  {"WtInterpCreateScript", WtInterpCreateCfg, NULL, OR_FILEINFO, TAKE1,
    "Script to run when an interpreter is created. Usage: WtInterpCreateScript SCRIPT"},
  {"WtInterpDestroyScript", WtInterpDestroyCfg, NULL, ACCESS_CONF, TAKE1,
    "Script to run when an interpreter is destroyed. Usage: WtInterpDestroyScript SCRIPT"},
  {"WtRequestStartScript", WtReqStartCfg, NULL, ACCESS_CONF, TAKE1,
    "Script to run when a request starts. Usage: WtRequestStartScript SCRIPT"},
  {"WtRequestEndScript", WtReqEndCfg, NULL, ACCESS_CONF, TAKE1,
    "Script to run when a request ends. Usage: WtRequestEndScript SCRIPT"},
  {"WtRequestErrorScript", WtReqErrorCfg, NULL, ACCESS_CONF, TAKE1,
    "Script to run when an error occurs during a request. Usage: WtRequestErrorScript SCRIPT"},
  {"WtPageStartScript", WtPageStartCfg, NULL, ACCESS_CONF, TAKE1,
    "Script to run when a page is executed. Usage: WtPageStartScript SCRIPT"},
  {"WtPageEndScript", WtPageEndCfg, NULL, ACCESS_CONF, TAKE1,
    "Script to run when a page exits. Usage: WtPageEndScript SCRIPT"},
  {NULL}
};

#ifdef WIN32

static DWORD tls_index;

BOOL WINAPI DllMain(HINSTANCE dllhandle, DWORD reason, LPVOID reserved)
{
  switch (reason) {
    case DLL_PROCESS_ATTACH:
      tls_index = TlsAlloc();
      if (tls_index == TLS_OUT_OF_INDEXES)
        return FALSE;
    case DLL_THREAD_ATTACH:
      TlsSetValue(tls_index, (LPVOID)0);
      break;
    case DLL_THREAD_DETACH:
      break;
    case DLL_PROCESS_DETACH:
      TlsFree(tls_index);
      break;
  }

  return TRUE;
}

WtThreadStorage *WtGetThreadStorage()
{
  WtThreadStorage *p = (WtThreadStorage *)TlsGetValue(tls_index);
  if (!p) {
    p = (WtThreadStorage *)calloc(1, sizeof(WtThreadStorage));
    p->context = NULL;
    TlsSetValue(tls_index, p);
  }
  return p;
}

#endif /* WIN32 */

module MODULE_VAR_EXPORT wtcl_module =
{
  STANDARD_MODULE_STUFF,
  wtcl_init,                 /* module initializer */
  wtcl_create_dir_config,
  wtcl_merge_dir_config,
  wtcl_create_server_config,
  wtcl_merge_server_config,
  wtcl_cmds,
  wtcl_handlers,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
#if MODULE_MAGIC_NUMBER >= 19970103
  NULL,
#endif
#if MODULE_MAGIC_NUMBER >= 19970719
  wtcl_child_init,         /* process initializer */
#endif
#if MODULE_MAGIC_NUMBER >= 19970728
  wtcl_child_exit,         /* process exit/cleanup */
#endif
#if MODULE_MAGIC_NUMBER >= 19970902
  NULL
#endif
};
